
/*
240328->0423 scale ex
*/
#include <stdint.h>
#include <functional>
#include <random>
#include <exception>
#include "seg.hpp"
#include <iso646.h>

#ifdef EASYXGUI_USINGOMP
#include <omp.h>
#endif

enum EasyxGUI_CTRLID {
    EGI_TEXT, EGI_BUTTON, EGI_COLORSELECTOR, EGI_CHECK, EGI_LABEL
};

typedef struct easyxgui_ctrlproperties
{
    int8_t etype;
    union
    {
        struct __text
        {
            int x;
            int y;
            int w;
            int h;
            TCHAR *s;
            COLORREF bg;
            COLORREF fg;
            COLORREF bd;
        }text,button,label;
        struct __colorselector
        {
            int x;
            int y;
            int w;
            int h;
            TCHAR *s;
            COLORREF bg;
            COLORREF fg;
            COLORREF bd;
            int x2;
            int y2;
            int w2;
            int h2;
            int h_h;
            bool isHorizon;//false
            uint8_t r;
            uint8_t g;
            uint8_t b;
        }colorselector;
        struct __checker
        {
            int x;
            int y;
            int x2;//dx x->box
            int W;// w of box
            TCHAR *s;
            COLORREF textcolor;
            COLORREF box;
            bool lefttext;
            bool isSelected;
        }checker;
        
    };
    TCHAR *helptext;
    bool autorender; // true
}EasyCtrl;
//int a = sizeof(EasyCtrl);
//#define len 10
//template <int len>

std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<int> rint0to100(0, 100);

inline int randint0to100()
{
    return rint0to100(gen);
}

int RandInt(int _min, int _max)
{
    std::uniform_int_distribution<int> rints(_min, _max);
    return rints(gen);
}
COLORREF RandomColor(uint8_t _min = 0, uint8_t _max = 255)
{
    uint8_t r,g,b;
    r = RandInt(_min, _max);
    g = RandInt(_min, _max);
    b = RandInt(_min, _max);
    return RGB(r,g,b);
}

/*
EasyGUI 240309
*/
class easygui {
    public:
        std::function<int(EasyCtrl*, ExMessage*, int*)> callback_f;
        COLORREF BGC;
        EasyCtrl *ecs;
        int len;
        int idx;
        int last_gid;//上一个改变的id
        // if return, ret 'true' in callback fn.
        easygui(unsigned int len, COLORREF background_color,
            const std::function<int(EasyCtrl*, ExMessage*, int *)> &fn): callback_f(fn) {
            this->idx = 0;
            this->len = len;
            this->BGC = background_color;
            this->ecs = new EasyCtrl[len];
        }
        ~easygui(){
            delete[] this->ecs;
        }
        void pack_text
        (int x,int y,int w,int h,TCHAR*s,COLORREF bg,COLORREF fg,COLORREF bd,TCHAR*helptext=nullptr)
        {
            if (this->idx >= this->len) {
                return;
            }
            this->ecs[this->idx].etype = EGI_TEXT;
            this->ecs[this->idx].autorender = true;
            this->ecs[this->idx].text.x = x;
            this->ecs[this->idx].text.y = y;
            this->ecs[this->idx].text.w = w;
            this->ecs[this->idx].text.h = h;
            this->ecs[this->idx].text.s = s;
            this->ecs[this->idx].text.bg = bg;
            this->ecs[this->idx].text.fg = fg;
            this->ecs[this->idx].text.bd = bd;
            this->ecs[this->idx].helptext = helptext;
            this->idx ++;
        }
        void pack_button
        (int x,int y,int w,int h,TCHAR*s,COLORREF bg,COLORREF fg,COLORREF bd,TCHAR*helptext=nullptr)
        {
            if (this->idx >= this->len) {
                return;
            }
            this->ecs[this->idx].etype = EGI_BUTTON;
            this->ecs[this->idx].autorender = true;
            this->ecs[this->idx].button.x = x;
            this->ecs[this->idx].button.y = y;
            this->ecs[this->idx].button.w = w;
            this->ecs[this->idx].button.h = h;
            this->ecs[this->idx].button.s = s;
            this->ecs[this->idx].button.bg = bg;
            this->ecs[this->idx].button.fg = fg;
            this->ecs[this->idx].button.bd = bd;
            this->ecs[this->idx].helptext = helptext;
            this->idx ++;
        }
        void pack_colorselector
        (int x, int y, int w, int h, TCHAR* s, COLORREF bg, COLORREF fg, COLORREF bd,
            int x2, int y2, int w2, int h2, int h_h, COLORREF value, TCHAR *helptext = nullptr)
        {
            if (this->idx >= this->len) {
                return;
            }
            this->ecs[this->idx].etype = EGI_COLORSELECTOR;
            this->ecs[this->idx].autorender = true;
            this->ecs[this->idx].colorselector.x = x;
            this->ecs[this->idx].colorselector.y = y;
            this->ecs[this->idx].colorselector.w = w;
            this->ecs[this->idx].colorselector.h = h;
            this->ecs[this->idx].colorselector.s = s;
            this->ecs[this->idx].colorselector.bg = bg;
            this->ecs[this->idx].colorselector.fg = fg;
            this->ecs[this->idx].colorselector.bd = bd;
            this->ecs[this->idx].colorselector.x2 = x2;
            this->ecs[this->idx].colorselector.y2 = y2;
            this->ecs[this->idx].colorselector.w2 = w2;
            this->ecs[this->idx].colorselector.h2 = h2;
            this->ecs[this->idx].colorselector.h_h = h_h;
            this->ecs[this->idx].colorselector.isHorizon = true;
            this->ecs[this->idx].helptext = helptext;
            this->ecs[this->idx].colorselector.r = GetRValue(value);
            this->ecs[this->idx].colorselector.g = GetGValue(value);
            this->ecs[this->idx].colorselector.b = GetBValue(value);
            this->idx ++;
        }
        void pack_checker
        (int x, int y, int x2, int W, TCHAR *s, COLORREF textc, COLORREF boxc,
            bool isSelected, TCHAR* helptext=nullptr)
        {
            if (this->idx >= this->len) {
                return;
            }
            this->ecs[this->idx].etype = EGI_CHECK;
            this->ecs[this->idx].autorender = true;
            this->ecs[this->idx].checker.x = x;
            this->ecs[this->idx].checker.y = y;
            this->ecs[this->idx].checker.x2 = x2;
            this->ecs[this->idx].checker.W = W;
            this->ecs[this->idx].checker.s = s;
            this->ecs[this->idx].checker.textcolor = textc;
            this->ecs[this->idx].checker.box = boxc;
            this->ecs[this->idx].checker.isSelected = isSelected;
            this->ecs[this->idx].helptext = helptext;
            this->idx++;
        }
        void pack_label
        (int x,int y,TCHAR*s,COLORREF fg,TCHAR*helptext=nullptr)
        {
            if (this->idx >= this->len) {
                return;
            }
            this->ecs[this->idx].etype = EGI_LABEL;
            this->ecs[this->idx].autorender = true;
            this->ecs[this->idx].label.x = x;
            this->ecs[this->idx].label.y = y;
            this->ecs[this->idx].label.s = s;
            this->ecs[this->idx].label.fg = fg;
            this->ecs[this->idx].helptext = helptext;
            this->idx ++;
        }
        int get(int gid, EasyCtrl *value)//success return true
        {
            if (gid < 0 || gid >= this->idx) {
                return 0;
            }
            value = &this->ecs[gid];
            return true;
        }
        void mainloop()
        {
            ExMessage msg;
            bool isclicked;
            while (true)
            {
                seg::render::fill(this->BGC);
                isclicked = false;
                this->last_gid = -1;
                getmessage(&msg);
                if (msg.message == WM_LBUTTONDOWN) {
                    isclicked = true;
                }
                for (int i = 0; i < this->idx; i++)
                {
                    // render & event
                    switch (this->ecs[i].etype)
                    {
                    case EGI_TEXT:
                    {
                        this->render_text(i);
                    }
                        break;
                    
                    case EGI_BUTTON:
                    {
                        if (isclicked) {
                            this->render_button(i, &msg);
                        } else {
                            this->render_button(i, nullptr);
                        }
                    } break;
                    
                    case EGI_COLORSELECTOR:
                    {
                        if (isclicked) {
                            this->render_colorchooser(i, &msg);
                        } else {
                            this->render_colorchooser(i, nullptr);
                        }
                    } break;
                    
                    case EGI_CHECK:
                    {
                        if (isclicked) {
                            this->render_checker(i, &msg);
                        } else {
                            this->render_checker(i, nullptr);
                        }
                    } break;
                    
                    case EGI_LABEL: {
                        this->render_label(i);
                    } break;
                    
                    default:
                        break;
                    }
                }
                if (this->last_gid != -1) {
                    FlushBatchDraw();
                    if (this->callback_f(&this->ecs[this->last_gid], &msg, &this->last_gid)) {
                        return;
                    }
                } else {
                    FlushBatchDraw();
                }
            }
            
        }
        void wait(const std::function<void()> &render_fn, DWORD sleep_ms = 20)
        {
            ExMessage msg;
            bool isclicked;
            bool ispeeked;
            while (true)
            {
                //seg::render::fill(this->BGC);
                render_fn();
                isclicked = false;
                this->last_gid = -1;
                Sleep(sleep_ms);
                ispeeked = peekmessage(&msg);
                if (ispeeked){
                    if (msg.message == WM_LBUTTONDOWN) {
                        isclicked = true;
                    }
                }
                for (int i = 0; i < this->idx; i++)
                {
                    // render & event
                    switch (this->ecs[i].etype)
                    {
                    case EGI_TEXT:
                    {
                        this->render_text(i);
                    }
                        break;
                    
                    case EGI_BUTTON:
                    {
                        if (isclicked) {
                            this->render_button(i, &msg);
                        } else {
                            this->render_button(i, nullptr);
                        }
                    } break;
                    
                    case EGI_COLORSELECTOR:
                    {
                        if (isclicked) {
                            this->render_colorchooser(i, &msg);
                        } else {
                            this->render_colorchooser(i, nullptr);
                        }
                    } break;
                    
                    case EGI_CHECK:
                    {
                        if (isclicked) {
                            this->render_checker(i, &msg);
                        } else {
                            this->render_checker(i, nullptr);
                        }
                    } break;
                    
                    case EGI_LABEL: {
                        this->render_label(i);
                    } break;
                    
                    default:
                        break;
                    }
                }
                if (this->last_gid != -1) {
                    FlushBatchDraw();
                    if (this->callback_f(&this->ecs[this->last_gid], &msg, &this->last_gid)) {
                        return;
                    }
                } else {
                    FlushBatchDraw();
                }
            }
            
        }
    public:
    protected:
        void render_text(int gid)
        {
            setlinecolor(this->ecs[gid].text.bd);
            setfillcolor(this->ecs[gid].text.bg);
            //EasyCtrl::__text &e = this->ecs[gid].text;
            #define e this->ecs[gid].text
            fillrectangle(e.x,e.y, e.x + e.w, e.y + e.h);
            settextcolor(this->ecs[gid].text.fg);
            seg::render::text(e.x+e.w/2, e.y + e.h/2, e.s, seg::render::Place::center);
            #undef e
        }
        void render_button(int gid, ExMessage *msg = nullptr)
        {
            setlinecolor(this->ecs[gid].button.bd);
            setfillcolor(this->ecs[gid].button.bg);
            //EasyCtrl::__text &e = this->ecs[gid].button;
            #define e this->ecs[gid].button
            fillrectangle(e.x,e.y, e.x + e.w, e.y + e.h);
            settextcolor(this->ecs[gid].button.fg);
            if (msg != nullptr) {
                if (e.x <= msg->x && msg->x <= e.x + e.w  &&  e.y <= msg->y && msg->y <= e.y + e.h) {
                    this->last_gid = gid;
                }
            }
            seg::render::text(e.x+e.w/2, e.y + e.h/2, e.s, seg::render::Place::center);
            #undef e
        }
        void render_checker(int gid, ExMessage *msg = nullptr)
        {
            settextcolor(this->ecs[gid].checker.textcolor);
            //EasyCtrl::__checker &e = this->ecs[gid].checker;
            #define e this->ecs[gid].checker
            seg::render::text(e.x, e.y, e.s, seg::render::Place::center);
            setlinecolor(e.box);
            setfillcolor(e.box);
            int xx = 0;
            if (e.x2 == 0) {
                xx = e.x + textwidth(e.s)+6;
            } else {
                xx = e.x2;
            }
            const int hw = e.W / 2;
            rectangle(xx-1, e.y-hw-1, xx-1 + e.W + 2, e.y+hw+1);
            rectangle(xx, e.y-hw, xx+e.W, e.y+hw);
            if (msg != nullptr) {
                if (xx <= msg->x && msg->x <= xx + e.W  &&  e.y-hw <= msg->y && msg->y <= e.y + hw) {
                    e.isSelected = !e.isSelected;
                    this->last_gid = gid;
                }
            }
            if (e.isSelected) {
                fillrectangle(xx+3, e.y-hw+3,xx+e.W-4, e.y+hw-4);
            }
            #undef e
        }
        void render_colorchooser(int gid, ExMessage *msg = nullptr)
        {
            settextcolor(this->ecs[gid].colorselector.fg);
            //EasyCtrl::__colorselector &e = this->ecs[gid].colorselector;
            #define e this->ecs[gid].colorselector
            setlinecolor(e.bd);
            setfillcolor(e.bg);
            fillrectangle(e.x, e.y, e.x + e.w, e.y + e.h);
            seg::render::text(e.x + e.w / 2, e.y + 1, e.s, seg::render::Place::top);
            int xx = e.x2;
            int yy = e.y2;
            TCHAR s[20];
            int perx;
            setfillcolor(RED);
            setlinecolor(e.bd);
            if (msg != nullptr) {
                if (msg->x >= xx && msg->x <= xx + e.w2  &&  msg->y >= yy && msg->y <= yy + e.h2) {
                    perx = int(float(msg->x - xx) / e.w2 * 255);
                    e.r = perx;
                    this->last_gid = gid;
                }
            }
            rectangle(xx-1, yy, xx + e.w2 + 1, yy + e.h2);
            setlinecolor(RED);
            fillrectangle(xx, yy+1, xx + e.w2*(e.r/255), yy + e.h2 - 1);
            settextcolor(RED);
            _stprintf(s, _T("%d"), e.r);
            seg::render::text(xx + e.w2 + 10, yy + e.h2/2, s, seg::render::Place::center);
            yy += e.h_h;
            
            setfillcolor(GREEN);
            setlinecolor(e.bd);
            if (msg != nullptr) {
                if (msg->x >= xx && msg->x <= xx + e.w2  &&  msg->y >= yy && msg->y <= yy + e.h2) {
                    perx = int(float(msg->x - xx) / e.w2 * 255);
                    e.g = perx;
                    this->last_gid = gid;
                }
            }
            rectangle(xx-1, yy, xx + e.w2 + 1, yy + e.h2);
            setlinecolor(GREEN);
            fillrectangle(xx, yy+1, xx + e.w2*(e.r/255), yy + e.h2 - 1);
            settextcolor(GREEN);
            _stprintf(s, _T("%d"), e.r);
            seg::render::text(xx + e.w2 + 10, yy + e.h2/2, s, seg::render::Place::center);
            yy += e.h_h;
            
            setfillcolor(BLUE);
            setlinecolor(e.bd);
            if (msg != nullptr) {
                if (msg->x >= xx && msg->x <= xx + e.w2  &&  msg->y >= yy && msg->y <= yy + e.h2) {
                    perx = int(float(msg->x - xx) / e.w2 * 255);
                    e.b = perx;
                    this->last_gid = gid;
                }
            }
            rectangle(xx-1, yy, xx + e.w2 + 1, yy + e.h2);
            setlinecolor(BLUE);
            fillrectangle(xx, yy+1, xx + e.w2*(e.r/255), yy + e.h2 - 1);
            settextcolor(BLUE);
            _stprintf(s, _T("%d"), e.r);
            seg::render::text(xx + e.w2 + 10, yy + e.h2/2, s, seg::render::Place::center);
            
            unsigned long color = RGB(e.r, e.g, e.b);
            _stprintf(s, _T("%lu"), color);
            settextcolor(color);
            seg::render::text(e.x + e.w/2, e.y + e.h -1, s, seg::render::Place::bottom);
            #undef e
        }
        void render_label(int gid)
        {
            #define e this->ecs[gid].label
            settextcolor(this->ecs[gid].label.fg);
            seg::render::text(e.x+e.w/2, e.y + e.h/2, e.s, seg::render::Place::center);
            #undef e
        }
};


typedef struct EasyColor__
{
    uint8_t r;
    uint8_t g;
    uint8_t b;
    uint8_t a;
}EasyColor;

/*
240322 image with colorkey
*/
class KeyImage {
    public:
        // width, height
        KeyImage(int w, int h){
            this->p = nullptr;
            this->w = w;
            this->h = h;
            if (this->w == 0 || this->h == 0) {
                throw std::exception("KeyImage.init:width or height get 0.");
            }
            this->im.Resize(w, h);
            this->p = new EasyColor[this->w * this->h];
        }
        // xflip from other KeyImage
        KeyImage(KeyImage &other) {
            this->p = nullptr;
            this->w = other.getw();
            this->h = other.geth();
            other.copy_im_to(&this->im);
            if (this->w != this->im.getwidth() || this->h != this->im.getheight()) {
                throw std::exception("KetImage.init(KeyImage&):width or height not match.");
            }
            this->p = new EasyColor[this->w * this->h];
            
            EasyColor *poi ;
            DWORD *ptr = GetImageBuffer(&this->im);
            for (int i = 0; i < this->w; i++)
            {
                for (int j = 0; j < this->h; j++)
                {
                    poi = &this->at(this->w - i - 1, j);
                    poi->r = GetRValue(ptr[i + this->w * j]);
                    poi->g = GetGValue(ptr[i + this->w * j]);
                    poi->b = GetBValue(ptr[i + this->w * j]);
                    poi->a = 255;
                }
            }
        }
        // load from file
        KeyImage(TCHAR *filename) {
            this->p = nullptr;
            loadimage(&this->im, filename);
            this->w = this->im.getwidth();
            this->h = this->im.getheight();
            if (this->w == 0 || this->h == 0) {
                throw std::exception("KeyImage.init(TCHAR*):width or height get 0.");
            }
            this->p = new EasyColor[this->w * this->h];
            
            EasyColor *poi ;
            DWORD *ptr = GetImageBuffer(&this->im);
            for (int i = 0; i < this->w; i++)
            {
                for (int j = 0; j < this->h; j++)
                {
                    poi = &this->at(i, j);
                    poi->r = GetRValue(ptr[i + this->w * j]);
                    poi->g = GetGValue(ptr[i + this->w * j]);
                    poi->b = GetBValue(ptr[i + this->w * j]);
                    poi->a = 255;
                }
            }
        }
        KeyImage(IMAGE *from) {
            this->p = nullptr;
            this->im = *from;
            this->w = this->im.getwidth();
            this->h = this->im.getheight();
            if (this->w == 0 || this->h == 0) {
                throw std::exception("KeyImage.init(IMAGE*):width or height get 0.");
            }
            this->p = new EasyColor[this->w * this->h];
            
            EasyColor *poi ;
            DWORD *ptr = GetImageBuffer(&this->im);
            for (int i = 0; i < this->w; i++)
            {
                for (int j = 0; j < this->h; j++)
                {
                    poi = &this->at(i, j);
                    poi->r = GetRValue(ptr[i + this->w * j]);
                    poi->g = GetGValue(ptr[i + this->w * j]);
                    poi->b = GetBValue(ptr[i + this->w * j]);
                    poi->a = 255;
                }
            }
        }
        ~KeyImage(){
            if (this->p != nullptr){
                delete[] this->p;
            }
        }
        // set colorkey
        void set_colorkey(COLORREF colorkey) {
            EasyColor *p;
            for (int i = 0; i < this->w; i++)
            {
                for (int j = 0; j < this->h; j++)
                {
                    p = &this->at(i, j);
                    if (RGB(p->r, p->g, p->b) == colorkey) {
                        p->a = 0;
                    } else {
                        p->a = 255;
                    }
                }
            }
        }
        // set alpha value when r,g,b greater than this
        void set_colorkey(uint8_t rv, uint8_t gv, uint8_t bv) {
            EasyColor *p;
            for (int i = 0; i < this->w; i++)
            {
                for (int j = 0; j < this->h; j++)
                {
                    p = &this->at(i, j);
                    if (p->r > rv && p->g > gv && p->b > bv) {
                        p->a = 0;
                    } else {
                        p->a = 255;
                    }
                }
            }
        }
        /* blit 'easycolor object' to screen.
        bt: blit type. 0: normal. 1: center.
        */
        void blit(int x, int y, int bt) {
            const int ww = getwidth();
            const int hh = getheight();
            DWORD *scr = GetImageBuffer();
            EasyColor *col;
            //printf("Blit! ");
            for (int iy = 0; iy < this->h; iy++) {
                //printf("blit y=%d! ", iy);
                for (int ix = 0; ix < this->w; ix++) {
                    //if(ix==0) printf("blit__! ");
                    col = &this->at(ix, iy);
                    //if(ix==0) printf("_blit! ");
                    int dx = x + ix;
                    int dy = y + iy;
                    const int offsetxy = dx + ww * dy;
                    if (0 <= dx && dx < ww  &&  0 <= dy && dy < hh
                        /*0 <= offsetxy && offsetxy < ww*hh*/  &&  col->a)
                    {
                        scr[dx + ww * dy] = RGB(col->r, col->g, col->b);
                    }
                }
            }
            //printf("\n");
        }
        // from codebus, have some bugs.
        void blit2(int x, int y) {
            DWORD* dst = GetImageBuffer();
            EasyColor *src = this->p;
            int dst_width = getwidth();
            int dst_height = getheight();
            int iwidth = (x + this->w > dst_width) ? dst_width - x : this->w;
            int iheight= (y + this->h > dst_height)? dst_height- y : this->h;
            dst += dst_width * y + x;
            for (int iy = 0; iy < iheight; iy++)
            {
                //printf(",blit at %dy", iy);
                for (int ix = 0; ix < iwidth; ix++)
                {
                    if (src[ix].a){
                        //printf("ix=%d ", ix);
                        dst[ix] = RGB(src[ix].r, src[ix].g, src[ix].b);
                    }
                }
                //printf("?blit_ \n");
                dst += dst_width;
                src += this->w;
            }
            //printf("\n");
        }
        // put 'im' to screen.
        void put(int x, int y) {
            putimage(x, y, &this->im);
        }
        EasyColor &at(int wx, int hy) {
            if (wx < 0 || hy < 0 || wx >= this->w || hy >= this->h) {
                throw std::out_of_range("KeyImage.at:w or h out of range.");
            }
            return this->p[wx + this->w * hy];
        }
        const int getw() {return this->w;}
        const int geth() {return this->h;}
        void copy_im_to(IMAGE *to) {
            *to = this->im;
        }
        // s: out message.
        // if ok, return 0
        int check_im(TCHAR *s) {
            int ret = 0;
            if (this->w == this->im.getwidth() && this->h == this->im.getheight()) {
                ret = 0;
            } else {
                ret = 1;
            }
            _stprintf(s, _T("Weight:(%d,%d), Image:(%d,%d)"),this->w,this->h,this->im.getwidth(),
                this->im.getheight());
            return ret;
        }
        void check_im() {
            _tprintf(_T("Weight:(%d,%d), Image:(%d,%d); "),this->w,this->h,this->im.getwidth(),
                this->im.getheight());
        }
    private:
        int w;
        int h;
        IMAGE im;
        EasyColor *p;
};



/* KeyImage support dark, light */
//#define __kex_w 1660
//#define __kex_h 940
template <int __kex_w, int __kex_h>
class KeyImageEx {
    private:
        IMAGE im;
        EasyColor p[__kex_w * __kex_h];
    public:
        KeyImageEx() {
            this->im.Resize(__kex_w, __kex_h);
        }
        KeyImageEx(KeyImage &other) {
            other.copy_im_to(&this->im);
            if (__kex_w != this->im.getwidth() || __kex_h != this->im.getheight()) {
                throw std::exception("KetImageEx.init(KeyImage&):width or height not match.");
            }
            
            EasyColor *poi ;
            DWORD *ptr = GetImageBuffer(&this->im);
            for (int i = 0; i < __kex_w; i++)
            {
                for (int j = 0; j < __kex_h; j++)
                {
                    poi = &this->at(__kex_w - i - 1, j);
                    poi->r = GetRValue(ptr[i + __kex_w * j]);
                    poi->g = GetGValue(ptr[i + __kex_w * j]);
                    poi->b = GetBValue(ptr[i + __kex_w * j]);
                    poi->a = 255;
                }
            }
        }
        // load from file
        KeyImageEx(TCHAR *filename) {
            loadimage(&this->im, filename);
            int w = this->im.getwidth();
            int h = this->im.getheight();
            if (w != __kex_w || h != __kex_h) {
                throw std::exception("KeyImageEx.init(TCHAR*):width or height not match.");
            }
            
            EasyColor *poi ;
            DWORD *ptr = GetImageBuffer(&this->im);
            for (int i = 0; i < __kex_w; i++)
            {
                for (int j = 0; j < __kex_h; j++)
                {
                    poi = &this->at(i, j);
                    poi->r = GetRValue(ptr[i + __kex_w * j]);
                    poi->g = GetGValue(ptr[i + __kex_w * j]);
                    poi->b = GetBValue(ptr[i + __kex_w * j]);
                    poi->a = 255;
                }
            }
        }
        KeyImageEx(IMAGE *from) {
            this->im = *from;
            int w = this->im.getwidth();
            int h = this->im.getheight();
            if (w != __kex_w || h != __kex_h) {
                throw std::exception("KeyImageEx.init(IMAGE*):width or height not match.");
            }
            
            EasyColor *poi ;
            DWORD *ptr = GetImageBuffer(&this->im);
            for (int i = 0; i < __kex_w; i++)
            {
                for (int j = 0; j < __kex_h; j++)
                {
                    poi = &this->at(i, j);
                    poi->r = GetRValue(ptr[i + __kex_w * j]);
                    poi->g = GetGValue(ptr[i + __kex_w * j]);
                    poi->b = GetBValue(ptr[i + __kex_w * j]);
                    poi->a = 255;
                }
            }
        }
        void begin(IMAGE *from) {
            int w = from->getwidth();
            int h = from->getheight();
            if (w != __kex_w || h != __kex_h) {
                throw std::exception("KeyImageEx.init(IMAGE*):width or height not match.");
            }
            
            EasyColor *poi ;
            DWORD *ptr = GetImageBuffer(from);
            for (int i = 0; i < __kex_w; i++)
            {
                for (int j = 0; j < __kex_h; j++)
                {
                    poi = &this->at(i, j);
                    poi->r = GetRValue(ptr[i + __kex_w * j]);
                    poi->g = GetGValue(ptr[i + __kex_w * j]);
                    poi->b = GetBValue(ptr[i + __kex_w * j]);
                    //poi->a = 255;
                }
            }
        }
        void begin(DWORD *ptr) {
            EasyColor *poi;
            for (int i = 0; i < __kex_w; i++)
            {
                for (int j = 0; j < __kex_h; j++)
                {
                    poi = &this->at(i, j);
                    poi->r = GetRValue(ptr[i + __kex_w * j]);
                    poi->g = GetGValue(ptr[i + __kex_w * j]);
                    poi->b = GetBValue(ptr[i + __kex_w * j]);
                    //poi->a = 255;
                }
            }
        }
        void begin() {
            EasyColor *poi ;
            DWORD *ptr = GetImageBuffer(&this->im);
            for (int i = 0; i < __kex_w; i++)
            {
                for (int j = 0; j < __kex_h; j++)
                {
                    poi = &this->at(i, j);
                    poi->r = GetRValue(ptr[i + __kex_w * j]);
                    poi->g = GetGValue(ptr[i + __kex_w * j]);
                    poi->b = GetBValue(ptr[i + __kex_w * j]);
                    //poi->a = 255;
                }
            }
        }
        // set colorkey
        void set_colorkey(COLORREF colorkey) {
            EasyColor *p;
            for (int i = 0; i < __kex_w; i++)
            {
                for (int j = 0; j < __kex_h; j++)
                {
                    p = &this->at(i, j);
                    if (RGB(p->r, p->g, p->b) == colorkey) {
                        p->a = 0;
                    } else {
                        p->a = 255;
                    }
                }
            }
        }
        // set alpha value when r,g,b greater than this
        void set_colorkey(uint8_t rv, uint8_t gv, uint8_t bv) {
            EasyColor *p;
            for (int i = 0; i < __kex_w; i++)
            {
                for (int j = 0; j < __kex_h; j++)
                {
                    p = &this->at(i, j);
                    if (p->r > rv && p->g > gv && p->b > bv) {
                        p->a = 0;
                    } else {
                        p->a = 255;
                    }
                }
            }
        }
        /* blit 'easycolor object' to screen.
        bt: blit type. 0: normal. 1: center.
        */
        void end(int x, int y) {
            const int ww = getwidth();
            const int hh = getheight();
            DWORD *scr = GetImageBuffer();
            EasyColor *col;
            //printf("Blit! ");
            #ifdef EASYXGUI_USINGOMP
            #pragma omp parallel for num_threads(5)
            #endif
            for (int iy = 0; iy < __kex_h; iy++) {
                //printf("blit y=%d! ", iy);
                for (int ix = 0; ix < __kex_w; ix++) {
                    //if(ix==0) printf("blit__! ");
                    col = &this->at(ix, iy);
                    //if(ix==0) printf("_blit! ");
                    int dx = x + ix;
                    int dy = y + iy;
                    const int offsetxy = dx + ww * dy;
                    if (0 <= dx && dx < ww  &&  0 <= dy && dy < hh
                        /*0 <= offsetxy && offsetxy < ww*hh*/  &&  col->a)
                    {
                        scr[dx + ww * dy] = RGB(col->r, col->g, col->b);
                    }
                }
            }
            //printf("\n");
        }
        // put 'im' to screen.
        void put(int x, int y) {
            putimage(x, y, &this->im);
        }
        EasyColor &at(int wx, int hy) {
            if (wx < 0 || hy < 0 || wx >= __kex_w || hy >= __kex_h) {
                throw std::out_of_range("KeyImage.at:w or h out of range.");
            }
            return this->p[wx + __kex_w * hy];
        }
        constexpr int getw() {return __kex_w;}
        constexpr int geth() {return __kex_h;}
        void copy_im_to(IMAGE *to) {
            *to = this->im;
        }
        // s: out message.
        // if ok, return 0
        int check_im(TCHAR *s) {
            int ret = 0;
            if (__kex_w == this->im.getwidth() && __kex_h == this->im.getheight()) {
                ret = 0;
            } else {
                ret = 1;
            }
            _stprintf(s, _T("Weight:(%d,%d), Image:(%d,%d)"),__kex_w,__kex_h,this->im.getwidth(),
                this->im.getheight());
            return ret;
        }
        void check_im() {
            _tprintf(_T("Weight:(%d,%d), Image:(%d,%d); "),__kex_w,__kex_h,this->im.getwidth(),
                this->im.getheight());
        }
        static uint8_t safesub(uint8_t value, uint8_t minus) {
            int i = value;
            i -= minus;
            if (i < 0) {
                i = 0;
            }
            return static_cast<uint8_t>(i);
        }
        static inline uint8_t safeadd(uint8_t value, uint8_t addition) {
            int_fast16_t i = value;
            i += addition;
            if (i > 255) {
                i = 255;
            }
            return static_cast<uint8_t>(i);
        }
        void dark(uint8_t moon) {
            EasyColor *col;
            for (int iy = 0; iy < __kex_h; iy++) {
                for (int ix = 0; ix < __kex_w; ix++) {
                    col = &this->at(ix, iy);
                    col->r = this->safesub(col->r, moon);
                    col->g = this->safesub(col->g, moon);
                    col->b = this->safesub(col->b, moon);
                }
            }
        }
        void light(const int x, const int y, uint8_t sun) {
            EasyColor *self;
            int xx = x;
            int yy = y;
            while (sun > 0) {
                xx --;
                while (xx < x) {
                    if (0 <= xx and xx < __kex_w  and  0 <= yy  and yy < __kex_h) {
                        self = &this->at(xx , yy);
                        self->r = this->safeadd(self->r, sun);
                        self->g = this->safeadd(self->g, sun);
                        self->b = this->safeadd(self->b, sun);
                    }
                    xx ++;
                    yy --;
                }
                while (yy < y) {
                    if (0 <= xx and xx < __kex_w  and  0 <= yy  and yy < __kex_h) {
                        self = &this->at(xx , yy);
                        self->r = this->safeadd(self->r, sun);
                        self->g = this->safeadd(self->g, sun);
                        self->b = this->safeadd(self->b, sun);
                    }
                    xx ++;
                    yy ++;
                }
                while (xx > x) {
                    if (0 <= xx and xx < __kex_w  and  0 <= yy  and yy < __kex_h) {
                        self = &this->at(xx , yy);
                        self->r = this->safeadd(self->r, sun);
                        self->g = this->safeadd(self->g, sun);
                        self->b = this->safeadd(self->b, sun);
                    }
                    xx --;
                    yy ++;
                }
                while (yy > y) {
                    if (0 <= xx and xx < __kex_w  and  0 <= yy  and yy < __kex_h) {
                        self = &this->at(xx , yy);
                        self->r = this->safeadd(self->r, sun);
                        self->g = this->safeadd(self->g, sun);
                        self->b = this->safeadd(self->b, sun);
                    }
                    xx --;
                    yy --;
                }
                sun--;
            }
        }
};

template <typename T> T mid(T value, T _min, T _max)
{
    if (value < _min) {
        return _min;
    } else if (value > _max) {
        return _max;
    } else return value;
}

/* scale KeyImageEx */
template<int a, int b, int c, int d>
void scale(KeyImageEx<a,b> *from, KeyImageEx<c,d> *to)
{
    /*
    (single)linear-interpotation method.
    */
    constexpr double perx = (double)(a) / double(c);
    constexpr double pery = (double)(b) / double(d);
    int xf, yf, xt, yt;
    if constexpr(perx < 1.0) {
        if constexpr(pery < 1.0) {
            // x+y+
            for (xt = 0; xt < c; xt++) {
                for (yt = 0; yt < d; yt++) {
                    to->at(xt, yt).r = from->at(mid<int>(int(xt * perx),0,a), mid<int>(int(yt * pery),0,b)).r;
                    to->at(xt, yt).g = from->at(mid<int>(int(xt * perx),0,a), mid<int>(int(yt * pery),0,b)).g;
                    to->at(xt, yt).b = from->at(mid<int>(int(xt * perx),0,a), mid<int>(int(yt * pery),0,b)).b;
                    to->at(xt, yt).a = from->at(mid<int>(int(xt * perx),0,a), mid<int>(int(yt * pery),0,b)).a;
                }
            }
        } else {
            // x+y-
            // 优化? 
            // 算了, 就这样吧
            for (xt = 0; xt < c; xt++) {
                for (yt = 0; yt < d; yt++) {
                    to->at(xt, yt).r = from->at(mid<int>(int(xt * perx),0,a), mid<int>(int(yt * pery),0,b)).r;
                    to->at(xt, yt).g = from->at(mid<int>(int(xt * perx),0,a), mid<int>(int(yt * pery),0,b)).g;
                    to->at(xt, yt).b = from->at(mid<int>(int(xt * perx),0,a), mid<int>(int(yt * pery),0,b)).b;
                    to->at(xt, yt).a = from->at(mid<int>(int(xt * perx),0,a), mid<int>(int(yt * pery),0,b)).a;
                }
            }
        }
    } else {
        for (xt = 0; xt < c; xt++) {
            for (yt = 0; yt < d; yt++) {
                to->at(xt, yt).r = from->at(mid<int>(int(xt * perx),0,a), mid<int>(int(yt * pery),0,b)).r;
                to->at(xt, yt).g = from->at(mid<int>(int(xt * perx),0,a), mid<int>(int(yt * pery),0,b)).g;
                to->at(xt, yt).b = from->at(mid<int>(int(xt * perx),0,a), mid<int>(int(yt * pery),0,b)).b;
                to->at(xt, yt).a = from->at(mid<int>(int(xt * perx),0,a), mid<int>(int(yt * pery),0,b)).a;
            }
        }
    }
}

// if s == null, reset (x,y) pos, else ignore pos and output s.
inline void xyprint(TCHAR *s = nullptr, int x = 0, int y = 0, int _dy = 0)
{
    static int xx;
    static int yy;
    //static int calcttt;
    if (s == nullptr) {
        xx = x;
        yy = y;
    } else {
        if (_dy == 0) {
            const int dy = textheight(s)+1;
            outtextxy(xx, yy, s);
            yy += dy;
        } else {
            yy += _dy;
        }
    }
    //calcttt++;
    //if (calcttt < 100) {
    //    printf("xyprint: y=%d\n", yy);
    //}
}